﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace WJP.DataPortal.Service
{
    /// <summary>
    /// 跨 C/S，B/S 的服务基类
    ///
    /// 注意，如果该服务要被使用到 B/S 上，输入和输出参数都应该是基本的数据类型、EntityList 类型。
    /// </summary>
    [Serializable]
    public abstract class Service : IService, IPortalUpdatable
    {
        [NonSerialized]
        private DataPortalLocation _dataPortalLocation;

        /// <summary>
        /// 当前服务是否需要在本地运行。（有时需要设置此值来强制服务在客户端运行。）
        ///
        /// 当在服务端时，默认值为 Local，表示直接在服务端运行。
        /// </summary>
        public DataPortalLocation DataPortalLocation
        {
            get
            {
                return this._dataPortalLocation;
            }
            set
            {
                this._dataPortalLocation = value;
            }
        }

        public Service()
        {
            _dataPortalLocation =
                PlatformEnvironment.NeetProxy() ? DataPortalLocation.Dynamic : DataPortalLocation.Local;
        }

        /// <summary>数据门户会调用此方法来实现执行逻辑。</summary>
        internal void ExecuteByDataPortal()
        {
            this.Execute();
            if (this.DataPortalLocation != DataPortalLocation.Dynamic)
                return;
            foreach (Type type in this.GetContractType())
            {
                foreach (PropertyInfo property in type.GetProperties())
                {
                    if (!ServiceHelper.IsOutput(property))
                    {
                        if (property.PropertyType.IsClass)
                        {
                            try
                            {
                                property.SetValue(this, null, null);
                            }
                            catch
                            {
                            }
                        }
                    }
                }
            }
        }

        /// <summary>子类重写此方法实现具体的业务逻辑</summary>
        protected abstract void Execute();

        private IEnumerable<Type> GetContractType()
        {
            Type serviceType = this.GetType();
            object[] list = serviceType.GetCustomAttributes(typeof(ContractImplAttribute), false);
            object[] objArray = list;
            for (int index = 0; index < objArray.Length; ++index)
            {
                ContractImplAttribute attri = (ContractImplAttribute)objArray[index];
                Type type = attri.ContractType;
                if (type is null)
                    type = serviceType;
                yield return type;
                attri = null;
            }
            objArray = null;
        }

        /// <summary>调用服务并把返回值转换为指定的类型。</summary>
        /// <returns></returns>
        public void Invoke()
        {
            this.OnInvoking();
            if (this.DataPortalLocation == DataPortalLocation.Local)
                this.Execute();
            else
                this.ReadOutput(DataPortalApi.Update((object)this, DataPortalLocation.Dynamic) as IService);
            this.OnInvoked();
        }

        /// <summary>使用反射把返回结果的值修改到当前对象上。</summary>
        /// <param name="res"></param>
        private void ReadOutput(IService res)
        {
            foreach (Type type in this.GetContractType())
            {
                foreach (PropertyInfo property in type.GetProperties())
                {
                    if (ServiceHelper.IsOutput(property))
                    {
                        object obj = property.GetValue(res, null);
                        try
                        {
                            property.SetValue(this, obj, null);
                        }
                        catch
                        {
                        }
                    }
                }
            }
        }

        /// <summary>在服务被调用前发生。</summary>
        protected virtual void OnInvoking()
        {
        }

        /// <summary>在服务被调用后发生。</summary>
        protected virtual void OnInvoked()
        {
        }

        void IPortalUpdatable.Update()
        {
            this.ExecuteByDataPortal();
        }
    }
}